package com.study.config;

import com.study.filter.TokenAuthenticationFilter;
import com.study.filter.TokenLoginFilter;
import com.study.security.DefaultPasswordEncoder;
import com.study.security.TokenLogoutHandler;
import com.study.security.TokenManager;
import com.study.security.UnauthEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

/**@EnableWebSecurity是Spring Security用于启用Web安全的注解
 *典型的用法是该注解用在某个Web安全配置类上(实现了接口WebSecurityConfigurer或者继承自WebSecurityConfigurerAdapter)
 * 要开启Spring方法级安全，在添加了@Configuration注解的类上再添加@EnableGlobalMethodSecurity注解即可
 */
@Configuration
@EnableWebSecurity//@EnableWebSecurity是Spring Security用于启用Web安全的注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private TokenManager tokenManager;
    private DefaultPasswordEncoder defaultPasswordEncoder;
    private UserDetailsService userDetailsService;
    private RedisTemplate redisTemplate;
@Autowired
    public TokenWebSecurityConfig(TokenManager tokenManager, DefaultPasswordEncoder defaultPasswordEncoder, UserDetailsService userDetailsService, RedisTemplate redisTemplate) {
        this.tokenManager = tokenManager;
        this.defaultPasswordEncoder = defaultPasswordEncoder;
        this.userDetailsService = userDetailsService;
        this.redisTemplate = redisTemplate;
    }
    /**configure配置
     * 设置退出地址和token，redis操作地址
     * 这段配置，我认为就是配置Security的认证策略, 每个模块配置使用and结尾。
     * authorizeRequests()配置路径拦截，表明路径访问所对应的权限，角色，认证信息。
     * formLogin()对应表单认证相关的配置
     * logout()对应了注销相关的配置
     * httpBasic()可以配置basic登录
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthEntryPoint())//没有权限访问时执行
                .and()
           .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers()
                .authenticated()
                .and()
           .logout()
                .logoutUrl("/admin/acl/index/logout")//设置退出路径
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate))
                .and()
           .addFilter(new TokenLoginFilter(tokenManager,authenticationManager(),redisTemplate))
                .addFilter(new TokenAuthenticationFilter(authenticationManager(),tokenManager,redisTemplate)).httpBasic()
                .and()
           .authorizeRequests()
                .antMatchers("/resources/**, /signup, /about").permitAll()
                .anyRequest().authenticated()
                .and();

    }
    /**
     * D调用userDetailService和密码处理
     * 这段配置呢, 配置的是认证信息
     * userDetailsService就是你自己写的类, 这个类的作用就是去获取用户信息,比如从数据库中获取。
     * 这样的话,AuthenticationManager在认证用户身份信息的时候，就回从中获取用户身份,和从http中拿的用户身份做对比。
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);

    }
    //不进行认证的路径，可以直接访问


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**");

    }
}
